// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#if V8_TARGET_ARCH_ARM64

#include "src/regexp/arm64/regexp-macro-assembler-arm64.h"

#include "src/arm64/macro-assembler-arm64-inl.h"
#include "src/log.h"
#include "src/macro-assembler.h"
#include "src/objects-inl.h"
#include "src/regexp/regexp-macro-assembler.h"
#include "src/regexp/regexp-stack.h"
#include "src/snapshot/embedded-data.h"
#include "src/unicode.h"

namespace v8 {
namespace internal {

    /*
 * This assembler uses the following register assignment convention:
 * - w19     : Used to temporarely store a value before a call to C code.
 *             See CheckNotBackReferenceIgnoreCase.
 * - x20     : Pointer to the current Code object,
 *             it includes the heap object tag.
 * - w21     : Current position in input, as negative offset from
 *             the end of the string. Please notice that this is
 *             the byte offset, not the character offset!
 * - w22     : Currently loaded character. Must be loaded using
 *             LoadCurrentCharacter before using any of the dispatch methods.
 * - x23     : Points to tip of backtrack stack.
 * - w24     : Position of the first character minus one: non_position_value.
 *             Used to initialize capture registers.
 * - x25     : Address at the end of the input string: input_end.
 *             Points to byte after last character in input.
 * - x26     : Address at the start of the input string: input_start.
 * - w27     : Where to start in the input string.
 * - x28     : Output array pointer.
 * - x29/fp  : Frame pointer. Used to access arguments, local variables and
 *             RegExp registers.
 * - x16/x17 : IP registers, used by assembler. Very volatile.
 * - sp      : Points to tip of C stack.
 *
 * - x0-x7   : Used as a cache to store 32 bit capture registers. These
 *             registers need to be retained every time a call to C code
 *             is done.
 *
 * The remaining registers are free for computations.
 * Each call to a public method should retain this convention.
 *
 * The stack will have the following structure:
 *
 *  Location    Name               Description
 *              (as referred to in
 *              the code)
 *
 *  - fp[96]   isolate            Address of the current isolate.
 *  ^^^ sp when called ^^^
 *  - fp[88]    lr                 Return from the RegExp code.
 *  - fp[80]    r29                Old frame pointer (CalleeSaved).
 *  - fp[0..72] r19-r28            Backup of CalleeSaved registers.
 *  - fp[-8]    direct_call        1 => Direct call from JavaScript code.
 *                                 0 => Call through the runtime system.
 *  - fp[-16]   stack_base         High end of the memory area to use as
 *                                 the backtracking stack.
 *  - fp[-24]   output_size        Output may fit multiple sets of matches.
 *  - fp[-32]   input              Handle containing the input string.
 *  - fp[-40]   success_counter
 *  ^^^^^^^^^^^^^ From here and downwards we store 32 bit values ^^^^^^^^^^^^^
 *  - fp[-44]   register N         Capture registers initialized with
 *  - fp[-48]   register N + 1     non_position_value.
 *              ...                The first kNumCachedRegisters (N) registers
 *              ...                are cached in x0 to x7.
 *              ...                Only positions must be stored in the first
 *  -           ...                num_saved_registers_ registers.
 *  -           ...
 *  -           register N + num_registers - 1
 *  ^^^^^^^^^ sp ^^^^^^^^^
 *
 * The first num_saved_registers_ registers are initialized to point to
 * "character -1" in the string (i.e., char_size() bytes before the first
 * character of the string). The remaining registers start out as garbage.
 *
 * The data up to the return address must be placed there by the calling
 * code and the remaining arguments are passed in registers, e.g. by calling the
 * code entry as cast to a function with the signature:
 * int (*match)(String input_string,
 *              int start_index,
 *              Address start,
 *              Address end,
 *              int* capture_output_array,
 *              int num_capture_registers,
 *              byte* stack_area_base,
 *              bool direct_call = false,
 *              Isolate* isolate);
 * The call is performed by NativeRegExpMacroAssembler::Execute()
 * (in regexp-macro-assembler.cc) via the GeneratedCode wrapper.
 */

#define __ ACCESS_MASM(masm_)

    const int RegExpMacroAssemblerARM64::kRegExpCodeSize;

    RegExpMacroAssemblerARM64::RegExpMacroAssemblerARM64(Isolate* isolate,
        Zone* zone, Mode mode,
        int registers_to_save)
        : NativeRegExpMacroAssembler(isolate, zone)
        , masm_(new MacroAssembler(isolate, CodeObjectRequired::kYes,
              NewAssemblerBuffer(kRegExpCodeSize)))
        , mode_(mode)
        , num_registers_(registers_to_save)
        , num_saved_registers_(registers_to_save)
        , entry_label_()
        , start_label_()
        , success_label_()
        , backtrack_label_()
        , exit_label_()
    {
        DCHECK_EQ(0, registers_to_save % 2);
        // We can cache at most 16 W registers in x0-x7.
        STATIC_ASSERT(kNumCachedRegisters <= 16);
        STATIC_ASSERT((kNumCachedRegisters % 2) == 0);
        __ B(&entry_label_); // We'll write the entry code later.
        __ Bind(&start_label_); // And then continue from here.
    }

    RegExpMacroAssemblerARM64::~RegExpMacroAssemblerARM64()
    {
        delete masm_;
        // Unuse labels in case we throw away the assembler without calling GetCode.
        entry_label_.Unuse();
        start_label_.Unuse();
        success_label_.Unuse();
        backtrack_label_.Unuse();
        exit_label_.Unuse();
        check_preempt_label_.Unuse();
        stack_overflow_label_.Unuse();
    }

    int RegExpMacroAssemblerARM64::stack_limit_slack()
    {
        return RegExpStack::kStackLimitSlack;
    }

    void RegExpMacroAssemblerARM64::AdvanceCurrentPosition(int by)
    {
        if (by != 0) {
            __ Add(current_input_offset(),
                current_input_offset(), by * char_size());
        }
    }

    void RegExpMacroAssemblerARM64::AdvanceRegister(int reg, int by)
    {
        DCHECK((reg >= 0) && (reg < num_registers_));
        if (by != 0) {
            RegisterState register_state = GetRegisterState(reg);
            switch (register_state) {
            case STACKED:
                __ Ldr(w10, register_location(reg));
                __ Add(w10, w10, by);
                __ Str(w10, register_location(reg));
                break;
            case CACHED_LSW: {
                Register to_advance = GetCachedRegister(reg);
                __ Add(to_advance, to_advance, by);
                break;
            }
            case CACHED_MSW: {
                Register to_advance = GetCachedRegister(reg);
                __ Add(to_advance, to_advance,
                    static_cast<int64_t>(by) << kWRegSizeInBits);
                break;
            }
            default:
                UNREACHABLE();
                break;
            }
        }
    }

    void RegExpMacroAssemblerARM64::Backtrack()
    {
        CheckPreemption();
        Pop(w10);
        __ Add(x10, code_pointer(), Operand(w10, UXTW));
        __ Br(x10);
    }

    void RegExpMacroAssemblerARM64::Bind(Label* label)
    {
        __ Bind(label);
    }

    void RegExpMacroAssemblerARM64::CheckCharacter(uint32_t c, Label* on_equal)
    {
        CompareAndBranchOrBacktrack(current_character(), c, eq, on_equal);
    }

    void RegExpMacroAssemblerARM64::CheckCharacterGT(uc16 limit,
        Label* on_greater)
    {
        CompareAndBranchOrBacktrack(current_character(), limit, hi, on_greater);
    }

    void RegExpMacroAssemblerARM64::CheckAtStart(Label* on_at_start)
    {
        __ Add(w10, current_input_offset(), Operand(-char_size()));
        __ Cmp(w10, string_start_minus_one());
        BranchOrBacktrack(eq, on_at_start);
    }

    void RegExpMacroAssemblerARM64::CheckNotAtStart(int cp_offset,
        Label* on_not_at_start)
    {
        __ Add(w10, current_input_offset(),
            Operand(-char_size() + cp_offset * char_size()));
        __ Cmp(w10, string_start_minus_one());
        BranchOrBacktrack(ne, on_not_at_start);
    }

    void RegExpMacroAssemblerARM64::CheckCharacterLT(uc16 limit, Label* on_less)
    {
        CompareAndBranchOrBacktrack(current_character(), limit, lo, on_less);
    }

    void RegExpMacroAssemblerARM64::CheckCharacters(Vector<const uc16> str,
        int cp_offset,
        Label* on_failure,
        bool check_end_of_string)
    {
        // This method is only ever called from the cctests.

        if (check_end_of_string) {
            // Is last character of required match inside string.
            CheckPosition(cp_offset + str.length() - 1, on_failure);
        }

        Register characters_address = x11;

        __ Add(characters_address,
            input_end(),
            Operand(current_input_offset(), SXTW));
        if (cp_offset != 0) {
            __ Add(characters_address, characters_address, cp_offset * char_size());
        }

        for (int i = 0; i < str.length(); i++) {
            if (mode_ == LATIN1) {
                __ Ldrb(w10, MemOperand(characters_address, 1, PostIndex));
                DCHECK_GE(String::kMaxOneByteCharCode, str[i]);
            } else {
                __ Ldrh(w10, MemOperand(characters_address, 2, PostIndex));
            }
            CompareAndBranchOrBacktrack(w10, str[i], ne, on_failure);
        }
    }

    void RegExpMacroAssemblerARM64::CheckGreedyLoop(Label* on_equal)
    {
        __ Ldr(w10, MemOperand(backtrack_stackpointer()));
        __ Cmp(current_input_offset(), w10);
        __ Cset(x11, eq);
        __ Add(backtrack_stackpointer(),
            backtrack_stackpointer(), Operand(x11, LSL, kWRegSizeLog2));
        BranchOrBacktrack(eq, on_equal);
    }

    void RegExpMacroAssemblerARM64::CheckNotBackReferenceIgnoreCase(
        int start_reg, bool read_backward, bool unicode, Label* on_no_match)
    {
        Label fallthrough;

        Register capture_start_offset = w10;
        // Save the capture length in a callee-saved register so it will
        // be preserved if we call a C helper.
        Register capture_length = w19;
        DCHECK(kCalleeSaved.IncludesAliasOf(capture_length));

        // Find length of back-referenced capture.
        DCHECK_EQ(0, start_reg % 2);
        if (start_reg < kNumCachedRegisters) {
            __ Mov(capture_start_offset.X(), GetCachedRegister(start_reg));
            __ Lsr(x11, GetCachedRegister(start_reg), kWRegSizeInBits);
        } else {
            __ Ldp(w11, capture_start_offset, capture_location(start_reg, x10));
        }
        __ Sub(capture_length, w11, capture_start_offset); // Length to check.

        // At this point, the capture registers are either both set or both cleared.
        // If the capture length is zero, then the capture is either empty or cleared.
        // Fall through in both cases.
        __ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);

        // Check that there are enough characters left in the input.
        if (read_backward) {
            __ Add(w12, string_start_minus_one(), capture_length);
            __ Cmp(current_input_offset(), w12);
            BranchOrBacktrack(le, on_no_match);
        } else {
            __ Cmn(capture_length, current_input_offset());
            BranchOrBacktrack(gt, on_no_match);
        }

        if (mode_ == LATIN1) {
            Label success;
            Label fail;
            Label loop_check;

            Register capture_start_address = x12;
            Register capture_end_addresss = x13;
            Register current_position_address = x14;

            __ Add(capture_start_address,
                input_end(),
                Operand(capture_start_offset, SXTW));
            __ Add(capture_end_addresss,
                capture_start_address,
                Operand(capture_length, SXTW));
            __ Add(current_position_address,
                input_end(),
                Operand(current_input_offset(), SXTW));
            if (read_backward) {
                // Offset by length when matching backwards.
                __ Sub(current_position_address, current_position_address,
                    Operand(capture_length, SXTW));
            }

            Label loop;
            __ Bind(&loop);
            __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex));
            __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex));
            __ Cmp(w10, w11);
            __ B(eq, &loop_check);

            // Mismatch, try case-insensitive match (converting letters to lower-case).
            __ Orr(w10, w10, 0x20); // Convert capture character to lower-case.
            __ Orr(w11, w11, 0x20); // Also convert input character.
            __ Cmp(w11, w10);
            __ B(ne, &fail);
            __ Sub(w10, w10, 'a');
            __ Cmp(w10, 'z' - 'a'); // Is w10 a lowercase letter?
            __ B(ls, &loop_check); // In range 'a'-'z'.
            // Latin-1: Check for values in range [224,254] but not 247.
            __ Sub(w10, w10, 224 - 'a');
            __ Cmp(w10, 254 - 224);
            __ Ccmp(w10, 247 - 224, ZFlag, ls); // Check for 247.
            __ B(eq, &fail); // Weren't Latin-1 letters.

            __ Bind(&loop_check);
            __ Cmp(capture_start_address, capture_end_addresss);
            __ B(lt, &loop);
            __ B(&success);

            __ Bind(&fail);
            BranchOrBacktrack(al, on_no_match);

            __ Bind(&success);
            // Compute new value of character position after the matched part.
            __ Sub(current_input_offset().X(), current_position_address, input_end());
            if (read_backward) {
                __ Sub(current_input_offset().X(), current_input_offset().X(),
                    Operand(capture_length, SXTW));
            }
            if (masm_->emit_debug_code()) {
                __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
                __ Ccmp(current_input_offset(), 0, NoFlag, eq);
                // The current input offset should be <= 0, and fit in a W register.
                __ Check(le, AbortReason::kOffsetOutOfRange);
            }
        } else {
            DCHECK(mode_ == UC16);
            int argument_count = 4;

            // The cached registers need to be retained.
            CPURegList cached_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 7);
            DCHECK_EQ(kNumCachedRegisters, cached_registers.Count() * 2);
            __ PushCPURegList(cached_registers);

            // Put arguments into arguments registers.
            // Parameters are
            //   x0: Address byte_offset1 - Address captured substring's start.
            //   x1: Address byte_offset2 - Address of current character position.
            //   w2: size_t byte_length - length of capture in bytes(!)
            //   x3: Isolate* isolate or 0 if unicode flag

            // Address of start of capture.
            __ Add(x0, input_end(), Operand(capture_start_offset, SXTW));
            // Length of capture.
            __ Mov(w2, capture_length);
            // Address of current input position.
            __ Add(x1, input_end(), Operand(current_input_offset(), SXTW));
            if (read_backward) {
                __ Sub(x1, x1, Operand(capture_length, SXTW));
            }
            // Isolate.
#ifdef V8_INTL_SUPPORT
            if (unicode) {
                __ Mov(x3, Operand(0));
            } else // NOLINT
#endif // V8_INTL_SUPPORT
            {
                __ Mov(x3, ExternalReference::isolate_address(isolate()));
            }

            {
                AllowExternalCallThatCantCauseGC scope(masm_);
                ExternalReference function = ExternalReference::re_case_insensitive_compare_uc16(isolate());
                __ CallCFunction(function, argument_count);
            }

            // Check if function returned non-zero for success or zero for failure.
            // x0 is one of the registers used as a cache so it must be tested before
            // the cache is restored.
            __ Cmp(x0, 0);
            __ PopCPURegList(cached_registers);
            BranchOrBacktrack(eq, on_no_match);

            // On success, advance position by length of capture.
            if (read_backward) {
                __ Sub(current_input_offset(), current_input_offset(), capture_length);
            } else {
                __ Add(current_input_offset(), current_input_offset(), capture_length);
            }
        }

        __ Bind(&fallthrough);
    }

    void RegExpMacroAssemblerARM64::CheckNotBackReference(int start_reg,
        bool read_backward,
        Label* on_no_match)
    {
        Label fallthrough;

        Register capture_start_address = x12;
        Register capture_end_address = x13;
        Register current_position_address = x14;
        Register capture_length = w15;

        // Find length of back-referenced capture.
        DCHECK_EQ(0, start_reg % 2);
        if (start_reg < kNumCachedRegisters) {
            __ Mov(x10, GetCachedRegister(start_reg));
            __ Lsr(x11, GetCachedRegister(start_reg), kWRegSizeInBits);
        } else {
            __ Ldp(w11, w10, capture_location(start_reg, x10));
        }
        __ Sub(capture_length, w11, w10); // Length to check.

        // At this point, the capture registers are either both set or both cleared.
        // If the capture length is zero, then the capture is either empty or cleared.
        // Fall through in both cases.
        __ CompareAndBranch(capture_length, Operand(0), eq, &fallthrough);

        // Check that there are enough characters left in the input.
        if (read_backward) {
            __ Add(w12, string_start_minus_one(), capture_length);
            __ Cmp(current_input_offset(), w12);
            BranchOrBacktrack(le, on_no_match);
        } else {
            __ Cmn(capture_length, current_input_offset());
            BranchOrBacktrack(gt, on_no_match);
        }

        // Compute pointers to match string and capture string
        __ Add(capture_start_address, input_end(), Operand(w10, SXTW));
        __ Add(capture_end_address,
            capture_start_address,
            Operand(capture_length, SXTW));
        __ Add(current_position_address,
            input_end(),
            Operand(current_input_offset(), SXTW));
        if (read_backward) {
            // Offset by length when matching backwards.
            __ Sub(current_position_address, current_position_address,
                Operand(capture_length, SXTW));
        }

        Label loop;
        __ Bind(&loop);
        if (mode_ == LATIN1) {
            __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex));
            __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex));
        } else {
            DCHECK(mode_ == UC16);
            __ Ldrh(w10, MemOperand(capture_start_address, 2, PostIndex));
            __ Ldrh(w11, MemOperand(current_position_address, 2, PostIndex));
        }
        __ Cmp(w10, w11);
        BranchOrBacktrack(ne, on_no_match);
        __ Cmp(capture_start_address, capture_end_address);
        __ B(lt, &loop);

        // Move current character position to position after match.
        __ Sub(current_input_offset().X(), current_position_address, input_end());
        if (read_backward) {
            __ Sub(current_input_offset().X(), current_input_offset().X(),
                Operand(capture_length, SXTW));
        }

        if (masm_->emit_debug_code()) {
            __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW));
            __ Ccmp(current_input_offset(), 0, NoFlag, eq);
            // The current input offset should be <= 0, and fit in a W register.
            __ Check(le, AbortReason::kOffsetOutOfRange);
        }
        __ Bind(&fallthrough);
    }

    void RegExpMacroAssemblerARM64::CheckNotCharacter(unsigned c,
        Label* on_not_equal)
    {
        CompareAndBranchOrBacktrack(current_character(), c, ne, on_not_equal);
    }

    void RegExpMacroAssemblerARM64::CheckCharacterAfterAnd(uint32_t c,
        uint32_t mask,
        Label* on_equal)
    {
        __ And(w10, current_character(), mask);
        CompareAndBranchOrBacktrack(w10, c, eq, on_equal);
    }

    void RegExpMacroAssemblerARM64::CheckNotCharacterAfterAnd(unsigned c,
        unsigned mask,
        Label* on_not_equal)
    {
        __ And(w10, current_character(), mask);
        CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal);
    }

    void RegExpMacroAssemblerARM64::CheckNotCharacterAfterMinusAnd(
        uc16 c,
        uc16 minus,
        uc16 mask,
        Label* on_not_equal)
    {
        DCHECK_GT(String::kMaxUtf16CodeUnit, minus);
        __ Sub(w10, current_character(), minus);
        __ And(w10, w10, mask);
        CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal);
    }

    void RegExpMacroAssemblerARM64::CheckCharacterInRange(
        uc16 from,
        uc16 to,
        Label* on_in_range)
    {
        __ Sub(w10, current_character(), from);
        // Unsigned lower-or-same condition.
        CompareAndBranchOrBacktrack(w10, to - from, ls, on_in_range);
    }

    void RegExpMacroAssemblerARM64::CheckCharacterNotInRange(
        uc16 from,
        uc16 to,
        Label* on_not_in_range)
    {
        __ Sub(w10, current_character(), from);
        // Unsigned higher condition.
        CompareAndBranchOrBacktrack(w10, to - from, hi, on_not_in_range);
    }

    void RegExpMacroAssemblerARM64::CheckBitInTable(
        Handle<ByteArray> table,
        Label* on_bit_set)
    {
        __ Mov(x11, Operand(table));
        if ((mode_ != LATIN1) || (kTableMask != String::kMaxOneByteCharCode)) {
            __ And(w10, current_character(), kTableMask);
            __ Add(w10, w10, ByteArray::kHeaderSize - kHeapObjectTag);
        } else {
            __ Add(w10, current_character(), ByteArray::kHeaderSize - kHeapObjectTag);
        }
        __ Ldrb(w11, MemOperand(x11, w10, UXTW));
        CompareAndBranchOrBacktrack(w11, 0, ne, on_bit_set);
    }

    bool RegExpMacroAssemblerARM64::CheckSpecialCharacterClass(uc16 type,
        Label* on_no_match)
    {
        // Range checks (c in min..max) are generally implemented by an unsigned
        // (c - min) <= (max - min) check
        switch (type) {
        case 's':
            // Match space-characters
            if (mode_ == LATIN1) {
                // One byte space characters are '\t'..'\r', ' ' and \u00a0.
                Label success;
                // Check for ' ' or 0x00A0.
                __ Cmp(current_character(), ' ');
                __ Ccmp(current_character(), 0x00A0, ZFlag, ne);
                __ B(eq, &success);
                // Check range 0x09..0x0D.
                __ Sub(w10, current_character(), '\t');
                CompareAndBranchOrBacktrack(w10, '\r' - '\t', hi, on_no_match);
                __ Bind(&success);
                return true;
            }
            return false;
        case 'S':
            // The emitted code for generic character classes is good enough.
            return false;
        case 'd':
            // Match ASCII digits ('0'..'9').
            __ Sub(w10, current_character(), '0');
            CompareAndBranchOrBacktrack(w10, '9' - '0', hi, on_no_match);
            return true;
        case 'D':
            // Match ASCII non-digits.
            __ Sub(w10, current_character(), '0');
            CompareAndBranchOrBacktrack(w10, '9' - '0', ls, on_no_match);
            return true;
        case '.': {
            // Match non-newlines (not 0x0A('\n'), 0x0D('\r'), 0x2028 and 0x2029)
            // Here we emit the conditional branch only once at the end to make branch
            // prediction more efficient, even though we could branch out of here
            // as soon as a character matches.
            __ Cmp(current_character(), 0x0A);
            __ Ccmp(current_character(), 0x0D, ZFlag, ne);
            if (mode_ == UC16) {
                __ Sub(w10, current_character(), 0x2028);
                // If the Z flag was set we clear the flags to force a branch.
                __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne);
                // ls -> !((C==1) && (Z==0))
                BranchOrBacktrack(ls, on_no_match);
            } else {
                BranchOrBacktrack(eq, on_no_match);
            }
            return true;
        }
        case 'n': {
            // Match newlines (0x0A('\n'), 0x0D('\r'), 0x2028 and 0x2029)
            // We have to check all 4 newline characters before emitting
            // the conditional branch.
            __ Cmp(current_character(), 0x0A);
            __ Ccmp(current_character(), 0x0D, ZFlag, ne);
            if (mode_ == UC16) {
                __ Sub(w10, current_character(), 0x2028);
                // If the Z flag was set we clear the flags to force a fall-through.
                __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne);
                // hi -> (C==1) && (Z==0)
                BranchOrBacktrack(hi, on_no_match);
            } else {
                BranchOrBacktrack(ne, on_no_match);
            }
            return true;
        }
        case 'w': {
            if (mode_ != LATIN1) {
                // Table is 256 entries, so all Latin1 characters can be tested.
                CompareAndBranchOrBacktrack(current_character(), 'z', hi, on_no_match);
            }
            ExternalReference map = ExternalReference::re_word_character_map(isolate());
            __ Mov(x10, map);
            __ Ldrb(w10, MemOperand(x10, current_character(), UXTW));
            CompareAndBranchOrBacktrack(w10, 0, eq, on_no_match);
            return true;
        }
        case 'W': {
            Label done;
            if (mode_ != LATIN1) {
                // Table is 256 entries, so all Latin1 characters can be tested.
                __ Cmp(current_character(), 'z');
                __ B(hi, &done);
            }
            ExternalReference map = ExternalReference::re_word_character_map(isolate());
            __ Mov(x10, map);
            __ Ldrb(w10, MemOperand(x10, current_character(), UXTW));
            CompareAndBranchOrBacktrack(w10, 0, ne, on_no_match);
            __ Bind(&done);
            return true;
        }
        case '*':
            // Match any character.
            return true;
        // No custom implementation (yet): s(UC16), S(UC16).
        default:
            return false;
        }
    }

    void RegExpMacroAssemblerARM64::Fail()
    {
        __ Mov(w0, FAILURE);
        __ B(&exit_label_);
    }

    Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source)
    {
        Label return_w0;
        // Finalize code - write the entry point code now we know how many
        // registers we need.

        // Entry code:
        __ Bind(&entry_label_);

        // Arguments on entry:
        // x0:  String   input
        // x1:  int      start_offset
        // x2:  byte*    input_start
        // x3:  byte*    input_end
        // x4:  int*     output array
        // x5:  int      output array size
        // x6:  Address  stack_base
        // x7:  int      direct_call

        //  sp[8]:  address of the current isolate
        //  sp[0]:  secondary link/return address used by native call

        // Tell the system that we have a stack frame.  Because the type is MANUAL, no
        // code is generated.
        FrameScope scope(masm_, StackFrame::MANUAL);

        // Push registers on the stack, only push the argument registers that we need.
        CPURegList argument_registers(x0, x5, x6, x7);

        CPURegList registers_to_retain = kCalleeSaved;
        DCHECK_EQ(11, kCalleeSaved.Count());
        registers_to_retain.Combine(lr);

        __ PushCPURegList(registers_to_retain);
        __ PushCPURegList(argument_registers);

        // Set frame pointer in place.
        __ Add(frame_pointer(), sp, argument_registers.Count() * kSystemPointerSize);

        // Initialize callee-saved registers.
        __ Mov(start_offset(), w1);
        __ Mov(input_start(), x2);
        __ Mov(input_end(), x3);
        __ Mov(output_array(), x4);

        // Set the number of registers we will need to allocate, that is:
        //   - success_counter (X register)
        //   - (num_registers_ - kNumCachedRegisters) (W registers)
        int num_wreg_to_allocate = num_registers_ - kNumCachedRegisters;
        // Do not allocate registers on the stack if they can all be cached.
        if (num_wreg_to_allocate < 0) {
            num_wreg_to_allocate = 0;
        }
        // Make room for the success_counter.
        num_wreg_to_allocate += 2;

        // Make sure the stack alignment will be respected.
        int alignment = masm_->ActivationFrameAlignment();
        DCHECK_EQ(alignment % 16, 0);
        int align_mask = (alignment / kWRegSize) - 1;
        num_wreg_to_allocate = (num_wreg_to_allocate + align_mask) & ~align_mask;

        // Check if we have space on the stack.
        Label stack_limit_hit;
        Label stack_ok;

        ExternalReference stack_limit = ExternalReference::address_of_stack_limit(isolate());
        __ Mov(x10, stack_limit);
        __ Ldr(x10, MemOperand(x10));
        __ Subs(x10, sp, x10);

        // Handle it if the stack pointer is already below the stack limit.
        __ B(ls, &stack_limit_hit);

        // Check if there is room for the variable number of registers above
        // the stack limit.
        __ Cmp(x10, num_wreg_to_allocate * kWRegSize);
        __ B(hs, &stack_ok);

        // Exit with OutOfMemory exception. There is not enough space on the stack
        // for our working registers.
        __ Mov(w0, EXCEPTION);
        __ B(&return_w0);

        __ Bind(&stack_limit_hit);
        CallCheckStackGuardState(x10);
        // If returned value is non-zero, we exit with the returned value as result.
        __ Cbnz(w0, &return_w0);

        __ Bind(&stack_ok);

        // Allocate space on stack.
        __ Claim(num_wreg_to_allocate, kWRegSize);

        // Initialize success_counter with 0.
        __ Str(wzr, MemOperand(frame_pointer(), kSuccessCounter));

        // Find negative length (offset of start relative to end).
        __ Sub(x10, input_start(), input_end());
        if (masm_->emit_debug_code()) {
            // Check that the size of the input string chars is in range.
            __ Neg(x11, x10);
            __ Cmp(x11, SeqTwoByteString::kMaxCharsSize);
            __ Check(ls, AbortReason::kInputStringTooLong);
        }
        __ Mov(current_input_offset(), w10);

        // The non-position value is used as a clearing value for the
        // capture registers, it corresponds to the position of the first character
        // minus one.
        __ Sub(string_start_minus_one(), current_input_offset(), char_size());
        __ Sub(string_start_minus_one(), string_start_minus_one(),
            Operand(start_offset(), LSL, (mode_ == UC16) ? 1 : 0));
        // We can store this value twice in an X register for initializing
        // on-stack registers later.
        __ Orr(twice_non_position_value(), string_start_minus_one().X(),
            Operand(string_start_minus_one().X(), LSL, kWRegSizeInBits));

        // Initialize code pointer register.
        __ Mov(code_pointer(), Operand(masm_->CodeObject()));

        Label load_char_start_regexp, start_regexp;
        // Load newline if index is at start, previous character otherwise.
        __ Cbnz(start_offset(), &load_char_start_regexp);
        __ Mov(current_character(), '\n');
        __ B(&start_regexp);

        // Global regexp restarts matching here.
        __ Bind(&load_char_start_regexp);
        // Load previous char as initial value of current character register.
        LoadCurrentCharacterUnchecked(-1, 1);
        __ Bind(&start_regexp);
        // Initialize on-stack registers.
        if (num_saved_registers_ > 0) {
            ClearRegisters(0, num_saved_registers_ - 1);
        }

        // Initialize backtrack stack pointer.
        __ Ldr(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackBase));

        // Execute
        __ B(&start_label_);

        if (backtrack_label_.is_linked()) {
            __ Bind(&backtrack_label_);
            Backtrack();
        }

        if (success_label_.is_linked()) {
            Register first_capture_start = w15;

            // Save captures when successful.
            __ Bind(&success_label_);

            if (num_saved_registers_ > 0) {
                // V8 expects the output to be an int32_t array.
                Register capture_start = w12;
                Register capture_end = w13;
                Register input_length = w14;

                // Copy captures to output.

                // Get string length.
                __ Sub(x10, input_end(), input_start());
                if (masm_->emit_debug_code()) {
                    // Check that the size of the input string chars is in range.
                    __ Cmp(x10, SeqTwoByteString::kMaxCharsSize);
                    __ Check(ls, AbortReason::kInputStringTooLong);
                }
                // input_start has a start_offset offset on entry. We need to include
                // it when computing the length of the whole string.
                if (mode_ == UC16) {
                    __ Add(input_length, start_offset(), Operand(w10, LSR, 1));
                } else {
                    __ Add(input_length, start_offset(), w10);
                }

                // Copy the results to the output array from the cached registers first.
                for (int i = 0;
                     (i < num_saved_registers_) && (i < kNumCachedRegisters);
                     i += 2) {
                    __ Mov(capture_start.X(), GetCachedRegister(i));
                    __ Lsr(capture_end.X(), capture_start.X(), kWRegSizeInBits);
                    if ((i == 0) && global_with_zero_length_check()) {
                        // Keep capture start for the zero-length check later.
                        __ Mov(first_capture_start, capture_start);
                    }
                    // Offsets need to be relative to the start of the string.
                    if (mode_ == UC16) {
                        __ Add(capture_start, input_length, Operand(capture_start, ASR, 1));
                        __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
                    } else {
                        __ Add(capture_start, input_length, capture_start);
                        __ Add(capture_end, input_length, capture_end);
                    }
                    // The output pointer advances for a possible global match.
                    __ Stp(capture_start, capture_end,
                        MemOperand(output_array(), kSystemPointerSize, PostIndex));
                }

                // Only carry on if there are more than kNumCachedRegisters capture
                // registers.
                int num_registers_left_on_stack = num_saved_registers_ - kNumCachedRegisters;
                if (num_registers_left_on_stack > 0) {
                    Register base = x10;
                    // There are always an even number of capture registers. A couple of
                    // registers determine one match with two offsets.
                    DCHECK_EQ(0, num_registers_left_on_stack % 2);
                    __ Add(base, frame_pointer(), kFirstCaptureOnStack);

                    // We can unroll the loop here, we should not unroll for less than 2
                    // registers.
                    STATIC_ASSERT(kNumRegistersToUnroll > 2);
                    if (num_registers_left_on_stack <= kNumRegistersToUnroll) {
                        for (int i = 0; i < num_registers_left_on_stack / 2; i++) {
                            __ Ldp(capture_end, capture_start,
                                MemOperand(base, -kSystemPointerSize, PostIndex));
                            if ((i == 0) && global_with_zero_length_check()) {
                                // Keep capture start for the zero-length check later.
                                __ Mov(first_capture_start, capture_start);
                            }
                            // Offsets need to be relative to the start of the string.
                            if (mode_ == UC16) {
                                __ Add(capture_start,
                                    input_length,
                                    Operand(capture_start, ASR, 1));
                                __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
                            } else {
                                __ Add(capture_start, input_length, capture_start);
                                __ Add(capture_end, input_length, capture_end);
                            }
                            // The output pointer advances for a possible global match.
                            __ Stp(capture_start, capture_end,
                                MemOperand(output_array(), kSystemPointerSize, PostIndex));
                        }
                    } else {
                        Label loop, start;
                        __ Mov(x11, num_registers_left_on_stack);

                        __ Ldp(capture_end, capture_start,
                            MemOperand(base, -kSystemPointerSize, PostIndex));
                        if (global_with_zero_length_check()) {
                            __ Mov(first_capture_start, capture_start);
                        }
                        __ B(&start);

                        __ Bind(&loop);
                        __ Ldp(capture_end, capture_start,
                            MemOperand(base, -kSystemPointerSize, PostIndex));
                        __ Bind(&start);
                        if (mode_ == UC16) {
                            __ Add(capture_start, input_length, Operand(capture_start, ASR, 1));
                            __ Add(capture_end, input_length, Operand(capture_end, ASR, 1));
                        } else {
                            __ Add(capture_start, input_length, capture_start);
                            __ Add(capture_end, input_length, capture_end);
                        }
                        // The output pointer advances for a possible global match.
                        __ Stp(capture_start, capture_end,
                            MemOperand(output_array(), kSystemPointerSize, PostIndex));
                        __ Sub(x11, x11, 2);
                        __ Cbnz(x11, &loop);
                    }
                }
            }

            if (global()) {
                Register success_counter = w0;
                Register output_size = x10;
                // Restart matching if the regular expression is flagged as global.

                // Increment success counter.
                __ Ldr(success_counter, MemOperand(frame_pointer(), kSuccessCounter));
                __ Add(success_counter, success_counter, 1);
                __ Str(success_counter, MemOperand(frame_pointer(), kSuccessCounter));

                // Capture results have been stored, so the number of remaining global
                // output registers is reduced by the number of stored captures.
                __ Ldr(output_size, MemOperand(frame_pointer(), kOutputSize));
                __ Sub(output_size, output_size, num_saved_registers_);
                // Check whether we have enough room for another set of capture results.
                __ Cmp(output_size, num_saved_registers_);
                __ B(lt, &return_w0);

                // The output pointer is already set to the next field in the output
                // array.
                // Update output size on the frame before we restart matching.
                __ Str(output_size, MemOperand(frame_pointer(), kOutputSize));

                if (global_with_zero_length_check()) {
                    // Special case for zero-length matches.
                    __ Cmp(current_input_offset(), first_capture_start);
                    // Not a zero-length match, restart.
                    __ B(ne, &load_char_start_regexp);
                    // Offset from the end is zero if we already reached the end.
                    __ Cbz(current_input_offset(), &return_w0);
                    // Advance current position after a zero-length match.
                    Label advance;
                    __ bind(&advance);
                    __ Add(current_input_offset(),
                        current_input_offset(),
                        Operand((mode_ == UC16) ? 2 : 1));
                    if (global_unicode())
                        CheckNotInSurrogatePair(0, &advance);
                }

                __ B(&load_char_start_regexp);
            } else {
                __ Mov(w0, SUCCESS);
            }
        }

        if (exit_label_.is_linked()) {
            // Exit and return w0
            __ Bind(&exit_label_);
            if (global()) {
                __ Ldr(w0, MemOperand(frame_pointer(), kSuccessCounter));
            }
        }

        __ Bind(&return_w0);

        // Set stack pointer back to first register to retain
        __ Mov(sp, fp);

        // Restore registers.
        __ PopCPURegList(registers_to_retain);

        __ Ret();

        Label exit_with_exception;
        // Registers x0 to x7 are used to store the first captures, they need to be
        // retained over calls to C++ code.
        CPURegList cached_registers(CPURegister::kRegister, kXRegSizeInBits, 0, 7);
        DCHECK_EQ(kNumCachedRegisters, cached_registers.Count() * 2);

        if (check_preempt_label_.is_linked()) {
            __ Bind(&check_preempt_label_);
            SaveLinkRegister();
            // The cached registers need to be retained.
            __ PushCPURegList(cached_registers);
            CallCheckStackGuardState(x10);
            // Returning from the regexp code restores the stack (sp <- fp)
            // so we don't need to drop the link register from it before exiting.
            __ Cbnz(w0, &return_w0);
            // Reset the cached registers.
            __ PopCPURegList(cached_registers);
            RestoreLinkRegister();
            __ Ret();
        }

        if (stack_overflow_label_.is_linked()) {
            __ Bind(&stack_overflow_label_);
            SaveLinkRegister();
            // The cached registers need to be retained.
            __ PushCPURegList(cached_registers);
            // Call GrowStack(backtrack_stackpointer(), &stack_base)
            __ Mov(x2, ExternalReference::isolate_address(isolate()));
            __ Add(x1, frame_pointer(), kStackBase);
            __ Mov(x0, backtrack_stackpointer());
            ExternalReference grow_stack = ExternalReference::re_grow_stack(isolate());
            __ CallCFunction(grow_stack, 3);
            // If return nullptr, we have failed to grow the stack, and
            // must exit with a stack-overflow exception.
            // Returning from the regexp code restores the stack (sp <- fp)
            // so we don't need to drop the link register from it before exiting.
            __ Cbz(w0, &exit_with_exception);
            // Otherwise use return value as new stack pointer.
            __ Mov(backtrack_stackpointer(), x0);
            // Reset the cached registers.
            __ PopCPURegList(cached_registers);
            RestoreLinkRegister();
            __ Ret();
        }

        if (exit_with_exception.is_linked()) {
            __ Bind(&exit_with_exception);
            __ Mov(w0, EXCEPTION);
            __ B(&return_w0);
        }

        CodeDesc code_desc;
        masm_->GetCode(isolate(), &code_desc);
        Handle<Code> code = isolate()->factory()->NewCode(code_desc, Code::REGEXP,
            masm_->CodeObject());
        PROFILE(masm_->isolate(),
            RegExpCodeCreateEvent(AbstractCode::cast(*code), *source));
        return Handle<HeapObject>::cast(code);
    }

    void RegExpMacroAssemblerARM64::GoTo(Label* to)
    {
        BranchOrBacktrack(al, to);
    }

    void RegExpMacroAssemblerARM64::IfRegisterGE(int reg, int comparand,
        Label* if_ge)
    {
        Register to_compare = GetRegister(reg, w10);
        CompareAndBranchOrBacktrack(to_compare, comparand, ge, if_ge);
    }

    void RegExpMacroAssemblerARM64::IfRegisterLT(int reg, int comparand,
        Label* if_lt)
    {
        Register to_compare = GetRegister(reg, w10);
        CompareAndBranchOrBacktrack(to_compare, comparand, lt, if_lt);
    }

    void RegExpMacroAssemblerARM64::IfRegisterEqPos(int reg, Label* if_eq)
    {
        Register to_compare = GetRegister(reg, w10);
        __ Cmp(to_compare, current_input_offset());
        BranchOrBacktrack(eq, if_eq);
    }

    RegExpMacroAssembler::IrregexpImplementation
    RegExpMacroAssemblerARM64::Implementation()
    {
        return kARM64Implementation;
    }

    void RegExpMacroAssemblerARM64::LoadCurrentCharacter(int cp_offset,
        Label* on_end_of_input,
        bool check_bounds,
        int characters)
    {
        // TODO(pielan): Make sure long strings are caught before this, and not
        // just asserted in debug mode.
        // Be sane! (And ensure that an int32_t can be used to index the string)
        DCHECK(cp_offset < (1 << 30));
        if (check_bounds) {
            if (cp_offset >= 0) {
                CheckPosition(cp_offset + characters - 1, on_end_of_input);
            } else {
                CheckPosition(cp_offset, on_end_of_input);
            }
        }
        LoadCurrentCharacterUnchecked(cp_offset, characters);
    }

    void RegExpMacroAssemblerARM64::PopCurrentPosition()
    {
        Pop(current_input_offset());
    }

    void RegExpMacroAssemblerARM64::PopRegister(int register_index)
    {
        Pop(w10);
        StoreRegister(register_index, w10);
    }

    void RegExpMacroAssemblerARM64::PushBacktrack(Label* label)
    {
        if (label->is_bound()) {
            int target = label->pos();
            __ Mov(w10, target + Code::kHeaderSize - kHeapObjectTag);
        } else {
            __ Adr(x10, label, MacroAssembler::kAdrFar);
            __ Sub(x10, x10, code_pointer());
            if (masm_->emit_debug_code()) {
                __ Cmp(x10, kWRegMask);
                // The code offset has to fit in a W register.
                __ Check(ls, AbortReason::kOffsetOutOfRange);
            }
        }
        Push(w10);
        CheckStackLimit();
    }

    void RegExpMacroAssemblerARM64::PushCurrentPosition()
    {
        Push(current_input_offset());
    }

    void RegExpMacroAssemblerARM64::PushRegister(int register_index,
        StackCheckFlag check_stack_limit)
    {
        Register to_push = GetRegister(register_index, w10);
        Push(to_push);
        if (check_stack_limit)
            CheckStackLimit();
    }

    void RegExpMacroAssemblerARM64::ReadCurrentPositionFromRegister(int reg)
    {
        RegisterState register_state = GetRegisterState(reg);
        switch (register_state) {
        case STACKED:
            __ Ldr(current_input_offset(), register_location(reg));
            break;
        case CACHED_LSW:
            __ Mov(current_input_offset(), GetCachedRegister(reg).W());
            break;
        case CACHED_MSW:
            __ Lsr(current_input_offset().X(), GetCachedRegister(reg),
                kWRegSizeInBits);
            break;
        default:
            UNREACHABLE();
            break;
        }
    }

    void RegExpMacroAssemblerARM64::ReadStackPointerFromRegister(int reg)
    {
        Register read_from = GetRegister(reg, w10);
        __ Ldr(x11, MemOperand(frame_pointer(), kStackBase));
        __ Add(backtrack_stackpointer(), x11, Operand(read_from, SXTW));
    }

    void RegExpMacroAssemblerARM64::SetCurrentPositionFromEnd(int by)
    {
        Label after_position;
        __ Cmp(current_input_offset(), -by * char_size());
        __ B(ge, &after_position);
        __ Mov(current_input_offset(), -by * char_size());
        // On RegExp code entry (where this operation is used), the character before
        // the current position is expected to be already loaded.
        // We have advanced the position, so it's safe to read backwards.
        LoadCurrentCharacterUnchecked(-1, 1);
        __ Bind(&after_position);
    }

    void RegExpMacroAssemblerARM64::SetRegister(int register_index, int to)
    {
        DCHECK(register_index >= num_saved_registers_); // Reserved for positions!
        Register set_to = wzr;
        if (to != 0) {
            set_to = w10;
            __ Mov(set_to, to);
        }
        StoreRegister(register_index, set_to);
    }

    bool RegExpMacroAssemblerARM64::Succeed()
    {
        __ B(&success_label_);
        return global();
    }

    void RegExpMacroAssemblerARM64::WriteCurrentPositionToRegister(int reg,
        int cp_offset)
    {
        Register position = current_input_offset();
        if (cp_offset != 0) {
            position = w10;
            __ Add(position, current_input_offset(), cp_offset * char_size());
        }
        StoreRegister(reg, position);
    }

    void RegExpMacroAssemblerARM64::ClearRegisters(int reg_from, int reg_to)
    {
        DCHECK(reg_from <= reg_to);
        int num_registers = reg_to - reg_from + 1;

        // If the first capture register is cached in a hardware register but not
        // aligned on a 64-bit one, we need to clear the first one specifically.
        if ((reg_from < kNumCachedRegisters) && ((reg_from % 2) != 0)) {
            StoreRegister(reg_from, string_start_minus_one());
            num_registers--;
            reg_from++;
        }

        // Clear cached registers in pairs as far as possible.
        while ((num_registers >= 2) && (reg_from < kNumCachedRegisters)) {
            DCHECK(GetRegisterState(reg_from) == CACHED_LSW);
            __ Mov(GetCachedRegister(reg_from), twice_non_position_value());
            reg_from += 2;
            num_registers -= 2;
        }

        if ((num_registers % 2) == 1) {
            StoreRegister(reg_from, string_start_minus_one());
            num_registers--;
            reg_from++;
        }

        if (num_registers > 0) {
            // If there are some remaining registers, they are stored on the stack.
            DCHECK_LE(kNumCachedRegisters, reg_from);

            // Move down the indexes of the registers on stack to get the correct offset
            // in memory.
            reg_from -= kNumCachedRegisters;
            reg_to -= kNumCachedRegisters;
            // We should not unroll the loop for less than 2 registers.
            STATIC_ASSERT(kNumRegistersToUnroll > 2);
            // We position the base pointer to (reg_from + 1).
            int base_offset = kFirstRegisterOnStack - kWRegSize - (kWRegSize * reg_from);
            if (num_registers > kNumRegistersToUnroll) {
                Register base = x10;
                __ Add(base, frame_pointer(), base_offset);

                Label loop;
                __ Mov(x11, num_registers);
                __ Bind(&loop);
                __ Str(twice_non_position_value(),
                    MemOperand(base, -kSystemPointerSize, PostIndex));
                __ Sub(x11, x11, 2);
                __ Cbnz(x11, &loop);
            } else {
                for (int i = reg_from; i <= reg_to; i += 2) {
                    __ Str(twice_non_position_value(),
                        MemOperand(frame_pointer(), base_offset));
                    base_offset -= kWRegSize * 2;
                }
            }
        }
    }

    void RegExpMacroAssemblerARM64::WriteStackPointerToRegister(int reg)
    {
        __ Ldr(x10, MemOperand(frame_pointer(), kStackBase));
        __ Sub(x10, backtrack_stackpointer(), x10);
        if (masm_->emit_debug_code()) {
            __ Cmp(x10, Operand(w10, SXTW));
            // The stack offset needs to fit in a W register.
            __ Check(eq, AbortReason::kOffsetOutOfRange);
        }
        StoreRegister(reg, w10);
    }

    // Helper function for reading a value out of a stack frame.
    template <typename T>
    static T& frame_entry(Address re_frame, int frame_offset)
    {
        return *reinterpret_cast<T*>(re_frame + frame_offset);
    }

    template <typename T>
    static T* frame_entry_address(Address re_frame, int frame_offset)
    {
        return reinterpret_cast<T*>(re_frame + frame_offset);
    }

    int RegExpMacroAssemblerARM64::CheckStackGuardState(
        Address* return_address, Address raw_code, Address re_frame,
        int start_index, const byte** input_start, const byte** input_end)
    {
        Code re_code = Code::cast(Object(raw_code));
        return NativeRegExpMacroAssembler::CheckStackGuardState(
            frame_entry<Isolate*>(re_frame, kIsolate), start_index,
            frame_entry<int>(re_frame, kDirectCall) == 1, return_address, re_code,
            frame_entry_address<Address>(re_frame, kInput), input_start, input_end);
    }

    void RegExpMacroAssemblerARM64::CheckPosition(int cp_offset,
        Label* on_outside_input)
    {
        if (cp_offset >= 0) {
            CompareAndBranchOrBacktrack(current_input_offset(),
                -cp_offset * char_size(), ge, on_outside_input);
        } else {
            __ Add(w12, current_input_offset(), Operand(cp_offset * char_size()));
            __ Cmp(w12, string_start_minus_one());
            BranchOrBacktrack(le, on_outside_input);
        }
    }

    // Private methods:

    void RegExpMacroAssemblerARM64::CallCheckStackGuardState(Register scratch)
    {
        DCHECK(!isolate()->IsGeneratingEmbeddedBuiltins());
        DCHECK(!masm_->options().isolate_independent_code);

        // Allocate space on the stack to store the return address. The
        // CheckStackGuardState C++ function will override it if the code
        // moved. Allocate extra space for 2 arguments passed by pointers.
        // AAPCS64 requires the stack to be 16 byte aligned.
        int alignment = masm_->ActivationFrameAlignment();
        DCHECK_EQ(alignment % 16, 0);
        int align_mask = (alignment / kXRegSize) - 1;
        int xreg_to_claim = (3 + align_mask) & ~align_mask;

        __ Claim(xreg_to_claim);

        // CheckStackGuardState needs the end and start addresses of the input string.
        __ Poke(input_end(), 2 * kSystemPointerSize);
        __ Add(x5, sp, 2 * kSystemPointerSize);
        __ Poke(input_start(), kSystemPointerSize);
        __ Add(x4, sp, kSystemPointerSize);

        __ Mov(w3, start_offset());
        // RegExp code frame pointer.
        __ Mov(x2, frame_pointer());
        // Code of self.
        __ Mov(x1, Operand(masm_->CodeObject()));

        // We need to pass a pointer to the return address as first argument.
        // DirectCEntry will place the return address on the stack before calling so
        // the stack pointer will point to it.
        __ Mov(x0, sp);

        DCHECK_EQ(scratch, x10);
        ExternalReference check_stack_guard_state = ExternalReference::re_check_stack_guard_state(isolate());
        __ Mov(scratch, check_stack_guard_state);

        if (FLAG_embedded_builtins) {
            UseScratchRegisterScope temps(masm_);
            Register scratch = temps.AcquireX();

            EmbeddedData d = EmbeddedData::FromBlob();
            CHECK(Builtins::IsIsolateIndependent(Builtins::kDirectCEntry));
            Address entry = d.InstructionStartOfBuiltin(Builtins::kDirectCEntry);

            __ Ldr(scratch, Operand(entry, RelocInfo::OFF_HEAP_TARGET));
            __ Call(scratch);
        } else {
            // TODO(v8:8519): Remove this once embedded builtins are on unconditionally.
            Handle<Code> code = BUILTIN_CODE(isolate(), DirectCEntry);
            __ Call(code, RelocInfo::CODE_TARGET);
        }

        // The input string may have been moved in memory, we need to reload it.
        __ Peek(input_start(), kSystemPointerSize);
        __ Peek(input_end(), 2 * kSystemPointerSize);

        __ Drop(xreg_to_claim);

        // Reload the Code pointer.
        __ Mov(code_pointer(), Operand(masm_->CodeObject()));
    }

    void RegExpMacroAssemblerARM64::BranchOrBacktrack(Condition condition,
        Label* to)
    {
        if (condition == al) { // Unconditional.
            if (to == nullptr) {
                Backtrack();
                return;
            }
            __ B(to);
            return;
        }
        if (to == nullptr) {
            to = &backtrack_label_;
        }
        __ B(condition, to);
    }

    void RegExpMacroAssemblerARM64::CompareAndBranchOrBacktrack(Register reg,
        int immediate,
        Condition condition,
        Label* to)
    {
        if ((immediate == 0) && ((condition == eq) || (condition == ne))) {
            if (to == nullptr) {
                to = &backtrack_label_;
            }
            if (condition == eq) {
                __ Cbz(reg, to);
            } else {
                __ Cbnz(reg, to);
            }
        } else {
            __ Cmp(reg, immediate);
            BranchOrBacktrack(condition, to);
        }
    }

    void RegExpMacroAssemblerARM64::CheckPreemption()
    {
        // Check for preemption.
        ExternalReference stack_limit = ExternalReference::address_of_stack_limit(isolate());
        __ Mov(x10, stack_limit);
        __ Ldr(x10, MemOperand(x10));
        __ Cmp(sp, x10);
        CallIf(&check_preempt_label_, ls);
    }

    void RegExpMacroAssemblerARM64::CheckStackLimit()
    {
        ExternalReference stack_limit = ExternalReference::address_of_regexp_stack_limit(isolate());
        __ Mov(x10, stack_limit);
        __ Ldr(x10, MemOperand(x10));
        __ Cmp(backtrack_stackpointer(), x10);
        CallIf(&stack_overflow_label_, ls);
    }

    void RegExpMacroAssemblerARM64::Push(Register source)
    {
        DCHECK(source.Is32Bits());
        DCHECK(!source.is(backtrack_stackpointer()));
        __ Str(source,
            MemOperand(backtrack_stackpointer(),
                -static_cast<int>(kWRegSize),
                PreIndex));
    }

    void RegExpMacroAssemblerARM64::Pop(Register target)
    {
        DCHECK(target.Is32Bits());
        DCHECK(!target.is(backtrack_stackpointer()));
        __ Ldr(target,
            MemOperand(backtrack_stackpointer(), kWRegSize, PostIndex));
    }

    Register RegExpMacroAssemblerARM64::GetCachedRegister(int register_index)
    {
        DCHECK_GT(kNumCachedRegisters, register_index);
        return Register::Create(register_index / 2, kXRegSizeInBits);
    }

    Register RegExpMacroAssemblerARM64::GetRegister(int register_index,
        Register maybe_result)
    {
        DCHECK(maybe_result.Is32Bits());
        DCHECK_LE(0, register_index);
        if (num_registers_ <= register_index) {
            num_registers_ = register_index + 1;
        }
        Register result = NoReg;
        RegisterState register_state = GetRegisterState(register_index);
        switch (register_state) {
        case STACKED:
            __ Ldr(maybe_result, register_location(register_index));
            result = maybe_result;
            break;
        case CACHED_LSW:
            result = GetCachedRegister(register_index).W();
            break;
        case CACHED_MSW:
            __ Lsr(maybe_result.X(), GetCachedRegister(register_index),
                kWRegSizeInBits);
            result = maybe_result;
            break;
        default:
            UNREACHABLE();
            break;
        }
        DCHECK(result.Is32Bits());
        return result;
    }

    void RegExpMacroAssemblerARM64::StoreRegister(int register_index,
        Register source)
    {
        DCHECK(source.Is32Bits());
        DCHECK_LE(0, register_index);
        if (num_registers_ <= register_index) {
            num_registers_ = register_index + 1;
        }

        RegisterState register_state = GetRegisterState(register_index);
        switch (register_state) {
        case STACKED:
            __ Str(source, register_location(register_index));
            break;
        case CACHED_LSW: {
            Register cached_register = GetCachedRegister(register_index);
            if (!source.Is(cached_register.W())) {
                __ Bfi(cached_register, source.X(), 0, kWRegSizeInBits);
            }
            break;
        }
        case CACHED_MSW: {
            Register cached_register = GetCachedRegister(register_index);
            __ Bfi(cached_register, source.X(), kWRegSizeInBits, kWRegSizeInBits);
            break;
        }
        default:
            UNREACHABLE();
            break;
        }
    }

    void RegExpMacroAssemblerARM64::CallIf(Label* to, Condition condition)
    {
        Label skip_call;
        if (condition != al)
            __ B(&skip_call, NegateCondition(condition));
        __ Bl(to);
        __ Bind(&skip_call);
    }

    void RegExpMacroAssemblerARM64::RestoreLinkRegister()
    {
        __ Pop(lr, xzr);
        __ Add(lr, lr, Operand(masm_->CodeObject()));
    }

    void RegExpMacroAssemblerARM64::SaveLinkRegister()
    {
        __ Sub(lr, lr, Operand(masm_->CodeObject()));
        __ Push(xzr, lr);
    }

    MemOperand RegExpMacroAssemblerARM64::register_location(int register_index)
    {
        DCHECK(register_index < (1 << 30));
        DCHECK_LE(kNumCachedRegisters, register_index);
        if (num_registers_ <= register_index) {
            num_registers_ = register_index + 1;
        }
        register_index -= kNumCachedRegisters;
        int offset = kFirstRegisterOnStack - register_index * kWRegSize;
        return MemOperand(frame_pointer(), offset);
    }

    MemOperand RegExpMacroAssemblerARM64::capture_location(int register_index,
        Register scratch)
    {
        DCHECK(register_index < (1 << 30));
        DCHECK(register_index < num_saved_registers_);
        DCHECK_LE(kNumCachedRegisters, register_index);
        DCHECK_EQ(register_index % 2, 0);
        register_index -= kNumCachedRegisters;
        int offset = kFirstCaptureOnStack - register_index * kWRegSize;
        // capture_location is used with Stp instructions to load/store 2 registers.
        // The immediate field in the encoding is limited to 7 bits (signed).
        if (is_int7(offset)) {
            return MemOperand(frame_pointer(), offset);
        } else {
            __ Add(scratch, frame_pointer(), offset);
            return MemOperand(scratch);
        }
    }

    void RegExpMacroAssemblerARM64::LoadCurrentCharacterUnchecked(int cp_offset,
        int characters)
    {
        Register offset = current_input_offset();

        // The ldr, str, ldrh, strh instructions can do unaligned accesses, if the CPU
        // and the operating system running on the target allow it.
        // If unaligned load/stores are not supported then this function must only
        // be used to load a single character at a time.

        // ARMv8 supports unaligned accesses but V8 or the kernel can decide to
        // disable it.
        // TODO(pielan): See whether or not we should disable unaligned accesses.
        if (!CanReadUnaligned()) {
            DCHECK_EQ(1, characters);
        }

        if (cp_offset != 0) {
            if (masm_->emit_debug_code()) {
                __ Mov(x10, cp_offset * char_size());
                __ Add(x10, x10, Operand(current_input_offset(), SXTW));
                __ Cmp(x10, Operand(w10, SXTW));
                // The offset needs to fit in a W register.
                __ Check(eq, AbortReason::kOffsetOutOfRange);
            } else {
                __ Add(w10, current_input_offset(), cp_offset * char_size());
            }
            offset = w10;
        }

        if (mode_ == LATIN1) {
            if (characters == 4) {
                __ Ldr(current_character(), MemOperand(input_end(), offset, SXTW));
            } else if (characters == 2) {
                __ Ldrh(current_character(), MemOperand(input_end(), offset, SXTW));
            } else {
                DCHECK_EQ(1, characters);
                __ Ldrb(current_character(), MemOperand(input_end(), offset, SXTW));
            }
        } else {
            DCHECK(mode_ == UC16);
            if (characters == 2) {
                __ Ldr(current_character(), MemOperand(input_end(), offset, SXTW));
            } else {
                DCHECK_EQ(1, characters);
                __ Ldrh(current_character(), MemOperand(input_end(), offset, SXTW));
            }
        }
    }

} // namespace internal
} // namespace v8

#undef __

#endif // V8_TARGET_ARCH_ARM64
